package com.wzdigit.wms.wms.service.order;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.wzdigit.framework.Shift;
import com.wzdigit.mes.basic.api.BasicSimpleCodeRuleApi;
import com.wzdigit.mes.sys.api.UserApiService;
import com.wzdigit.mes.sys.dto.UserDto;
import com.wzdigit.wms.basic.client.PagerInfo;
import com.wzdigit.wms.basic.client.request.gdn.other.*;
import com.wzdigit.wms.basic.client.response.basic.SkuResponse;
import com.wzdigit.wms.basic.client.response.gdn.other.*;
import com.wzdigit.wms.basic.domain.basic.Sku;
import com.wzdigit.wms.basic.domain.dtos.inventory.InventoryConditionDto;
import com.wzdigit.wms.basic.domain.dtos.inventory.UpdateInventoryInputDto;
import com.wzdigit.wms.basic.domain.inventory.*;
import com.wzdigit.wms.basic.domain.order.DocLog;
import com.wzdigit.wms.basic.domain.order.TrsnDtl;
import com.wzdigit.wms.basic.domain.order.TrsnDtlGoods;
import com.wzdigit.wms.basic.domain.order.TrsnHeader;
import com.wzdigit.wms.basic.domain.transfer.GdnDtl;
import com.wzdigit.wms.basic.domain.transfer.GdnDtlGoods;
import com.wzdigit.wms.basic.domain.transfer.GdnHeader;
import com.wzdigit.wms.basic.enums.*;
import com.wzdigit.wms.basic.service.code.InventoryService;
import com.wzdigit.wms.basic.service.code.LotnoService;
import com.wzdigit.wms.wms.service.across.CallbackService;
import com.wzdigit.wms.wms.service.basic.SkuService;
import com.wzdigit.wms.wms.service.core.*;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.core.utils.BeanUtil;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OtherStockOutService {

    @Reference(version = "${dubbo.consumers.basic.simple.code.rule.api:1.0.0}", check = false)
    BasicSimpleCodeRuleApi basicSimpleCodeRuleApi;
    @Reference(version = "${dubbo.consumers.sys.user.api.version:1.0.0}", check = false, timeout = 300000)
    UserApiService userApiService;
    final String trsnRuleCode = "TRSN_ORDER";

    @Autowired
    private GdnManagerService gdnManagerService;
    @Autowired
    private SkuService skuService;
    @Autowired
    private GoodsService goodsService;
    @Autowired
    private TrsnManagerService trsnManagerService;
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private DocLogService docLogService;
    @Autowired
    private LotnoService lotnoService;
    @Autowired
    private CallbackService callbackService;


    /**
     * 获取任务信息
     *
     * @param input
     * @param <T>
     * @return
     */
    public <T> PagerInfo<List<OtherOutOrderInfoResponse>> search(SearchOtherOutOrderRequest input) {
        Page<T> page = PageHelper.offsetPage(input.getSkipCount(), input.getMaxResultCount());
        PagerInfo<List<OtherOutOrderInfoResponse>> pagerInfo = new PagerInfo<>();
        List<OtherOutOrderInfoResponse> list = this.gdnManagerService.selectOtherTaskInfo(input);
        Map<String, String> userNames = new HashMap<>();
        for (OtherOutOrderInfoResponse item : list) {
            GdnTypeEnum enumByCode = GdnTypeEnum.getEnumByCode(item.getGdnType());
            if (enumByCode != null) {
                item.setGdnTypeName(enumByCode.getEnumName());
            }

            GdnHeaderStatusEnum enumByCode1 = GdnHeaderStatusEnum.getEnumByCode(item.getStatus());
            if (enumByCode1 != null) {
                item.setStatusName(enumByCode1.getEnumName());
            }
            //获取用户
            if (!userNames.containsKey(item.getCreateUser())) {
                UserDto userByUsername = this.userApiService.getUserByUsername(item.getCreateUser());
                if (userByUsername == null) {
                    userNames.put(item.getCreateUser(), item.getCreateUser());
                } else {
                    if (userByUsername.getUserId() == null)
                        userNames.put(item.getCreateUser(), item.getCreateUser());
                    else
                        userNames.put(item.getCreateUser(), userByUsername.getName());
                }
            }
            item.setCreateUser(userNames.get(item.getCreateUser()));
        }
        pagerInfo.setItems(list);
        pagerInfo.setTotalCount(page.getTotal());

        return pagerInfo;
    }

    /**
     * 扫描工单
     *
     * @param billNo
     * @return
     */
    public ScanOtherOutOrderResponse scanOrder(String billNo) {
        //1. 获取订单信息
        GdnHeader gdnHeader = this.gdnManagerService.selectByNo(billNo);
        if (gdnHeader == null) {
            Shift.fatal(String.format("没有找到出库单[%s]的信息!", billNo));
        }
        if (gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CLOSE.getCode())) {
            Shift.fatal(String.format("单据[%s]已关单,无需继续操作！", billNo));
        }
        if (!(gdnHeader.getGdnType().equals(GdnTypeEnum.D99.getCode())
                || gdnHeader.getGdnType().equals(GdnTypeEnum.D03.getCode())
                || gdnHeader.getGdnType().equals(GdnTypeEnum.D08.getCode())
                || gdnHeader.getGdnType().equals(GdnTypeEnum.D09.getCode())
                || gdnHeader.getGdnType().equals(GdnTypeEnum.D10.getCode())
        )) {
            Shift.fatal(String.format("单据[%s]不属于其他出库范围！", billNo));
        }
        //2.获取明细
        List<GdnDtl> gdnDtlList = this.gdnManagerService.selectDetailsByHeaderId(gdnHeader.getGdnHeaderId());
        if (gdnDtlList.size() == 0) {
            Shift.fatal(String.format("没有找到单号[%s]的明细信息！", billNo));
        }
        gdnDtlList.forEach(t -> {
            if (t.getShipQty() == null) {
                t.setShipQty(BigDecimal.ZERO);
            }
        });
        List<GdnDtl> collect = gdnDtlList.stream().filter(t -> t.getWhsId() != null && t.getModifyTime() != null)
                .sorted(Comparator.comparing(GdnDtl::getModifyTime).reversed())
                .collect(Collectors.toList());

        Integer whsId = collect.size() > 0 ? collect.get(0).getWhsId() : null;

        ScanOtherOutOrderResponse ret = new ScanOtherOutOrderResponse();
        ret.setGdnNo(gdnHeader.getGdnNo());
        ret.setSourceNo(gdnHeader.getSourceShipNotice());
        ret.setStatus(gdnHeader.getStatus());
        ret.setStatusName(GdnHeaderStatusEnum.getEnumByCode(gdnHeader.getStatus()).getEnumName());
        ret.setWhsId(whsId);
        ret.setOwner(gdnHeader.getOwner());
        ret.setFactory(gdnHeader.getFactory());
        ret.setGdnType(gdnHeader.getGdnType());
        ret.setGdnTypeName(GdnTypeEnum.getEnumByCode(ret.getGdnType()).getEnumName());

        if (gdnHeader.getCustomer() == null) {
            //TODO 获取客户信息

        }
        ret.setPlanQty(gdnDtlList.stream().map(GdnDtl::getOrderQty).reduce(BigDecimal.ZERO, BigDecimal::add));
        ret.setSubQty(gdnDtlList.stream().map(GdnDtl::getShipQty).reduce(BigDecimal.ZERO, BigDecimal::add));

        for (GdnDtl gdnDtl : gdnDtlList) {
            SkuResponse sku = this.skuService.getSku(gdnDtl.getSkuId());
            ScanOtherOutOrderItemsResponse item = new ScanOtherOutOrderItemsResponse();
            item.setId(gdnDtl.getGdnDtlId());
            item.setRowIndex(String.valueOf(gdnDtl.getGdnLineNo()));
            item.setSoNo(gdnDtl.getSoNo());
            item.setSkuId(gdnDtl.getSkuId());
            if ("".equals(item.getLotNo())) {
                Lotno lotno = lotnoService.getLotnoByGdnDtlId(gdnDtl.getGdnDtlId());
                if (lotno != null)
                    item.setLotNo(lotno.getLotno());
            } else {
                item.setLotNo(gdnDtl.getLotno());
            }
            item.setSkuDesc(sku.getDescCn());
            item.setSkuCode(sku.getSkuCode());

            item.setStatus(gdnDtl.getStatus());
            item.setStatusName(GdnDtlStatusEnum.getEnumByCode(gdnDtl.getStatus()).getEnumName());

            item.setPlanQty(gdnDtl.getOrderQty());
            item.setSubQty(gdnDtl.getShipQty());
            if (item.getSubQty() == null)
                item.setSubQty(BigDecimal.ZERO);
            item.setPoNo(gdnDtl.getPoNo());
            ret.getItems().add(item);
        }
        //TODO 获取数据字典扩展中对单据类型的能否进行无sn扫描收货开关
        ret.setSnScan(true);
        return ret;
    }

    /**
     * 获取已扫描出库的sn信息
     *
     * @param input
     * @param <T>
     * @return
     */
    public <T> PagerInfo<List<OtherOutOrderGoodsResponse>> searchOrderGoods(SearchOrderGoodsRequest input) {
        Page<T> page = PageHelper.offsetPage(input.getSkipCount(), input.getMaxResultCount());

        PagerInfo<List<OtherOutOrderGoodsResponse>> pagerInfo = new PagerInfo<>();

        List<OtherOutOrderGoodsResponse> list = this.gdnManagerService.selectGoodsByNo(input);

        pagerInfo.setItems(list);
        pagerInfo.setTotalCount(page.getTotal());

        return pagerInfo;
    }

    /**
     * 扫描sn
     *
     * @param input
     * @return
     */
    public ScanOtherOutSnResponse scanSn(SearchOtherOutSnRequest input) {
		// 1、获取sn信息
		Goods goods = goodsService.selectBySn(input.getSn());
		if (goods == null) {
			Shift.fatal("没有找到sn信息!");
		}
		if (!goods.getStatus().equals(GoodsStatusEnum.WAIT_UP.code)
				&& !goods.getStatus().equals(GoodsStatusEnum.HAS_UP.code)
				&& !goods.getStatus().equals(GoodsStatusEnum.WAIT_DOWM.code)) {
			Shift.fatal("sn状态不是“待上架”、“已上架”、“已下架”状态!");
		}
		if (!Objects.equals(input.getWhsId(), goods.getWhsId())) {
			Shift.fatal(String.format("SN[%s]不属于所选择的仓库!", input.getSn()));
		}

		// 2、获取发货单表头
		GdnHeader gdnHeader = gdnManagerService.selectByNo(input.getGdnNo());
		if (!Objects.equals(gdnHeader.getOwner(), goods.getOwner())) {
			Shift.fatal("sn所属组织与单据所属货主不一致!");
		}
		if (!Objects.equals(gdnHeader.getFactory(), goods.getFactory())) {
			Shift.fatal("sn所属组织与单据所属组织不一致!");
		}

		// 3、获取sku信息
        Sku sku = skuService.getSkuByPk(goods.getSkuId());
        
        // 响应结果
        ScanOtherOutSnResponse response = new ScanOtherOutSnResponse();
        response.setSkuId(sku.getSkuId());
        response.setSkuCode(sku.getSkuCode());
        response.setSkuDesc(sku.getDescCn());
        response.setGoodsSn(goods.getGoodsSn());
        response.setSoNo(goods.getSoNo());
        response.setPoNo(goods.getPoNo());
        response.setQty(goods.getQty());
        
        // 获取发货单明细
        List<Long> gdnDtlIdList = input.getCanMatchItems().stream().map(SearchOtherOutCanMatchItemRequest::getId).collect(Collectors.toList());
        List<GdnDtl> gdnDtlList = gdnManagerService.selectDetailByIds(gdnDtlIdList);
        for (GdnDtl gdnDtl : gdnDtlList) {
        	if(!Objects.equals(sku.getSkuId(), gdnDtl.getSkuId())) {
        		continue;
        	}
            SearchOtherOutCanMatchItemRequest canMatchItem = input.getCanMatchItems().stream().filter(t -> t.getId().equals(gdnDtl.getGdnDtlId())).findFirst().orElse(null);
            if (canMatchItem == null) {
            	continue;
            }
            if (gdnDtl.getShipQty() == null) {
            	gdnDtl.setShipQty(BigDecimal.ZERO);
            }
            if (gdnDtl.getOrderQty().subtract(gdnDtl.getShipQty()).compareTo(goods.getQty()) == -1) {
            	continue;
            }
            if(!Objects.equals(canMatchItem.getLotNo(), goods.getLotno())) {
            	continue;
            }
            if(!Objects.equals(gdnDtl.getQaStatus(), goods.getQaStatus())) {
            	continue;
            }
            response.setGdnDtlId(canMatchItem.getId());
            break;
        }
        if (response.getGdnDtlId() == null) {
        	Shift.fatal(String.format("SN[%s]没有匹配到对应的发货行或已超出待发数!", input.getSn()));
        }
        return response;
    }

    /**
     * 提交
     *
     * @param input
     * @throws Exception 
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public boolean submit(SubmitOtherOutInputRequest input) throws Exception {
        boolean isClose = false;
        //1, 获取单据
        GdnHeader gdnHeader = this.gdnManagerService.selectByNo(input.getGdnNo());
        if (gdnHeader == null) {
            Shift.fatal("没有找到单据信息!");
        }
        if (gdnHeader.getStatus().equals(GdnHeaderStatusEnum.FINISH.getCode()) ||
                gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CLOSE.getCode()) ||
                gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CANCEL.getCode())) {
            Shift.fatal("单据已经完成/关闭,不能进行出库!");
        }
        gdnHeader.setModifyUser(input.getUserName());
        //1.1 获取单据明细
        List<GdnDtl> gdnDtlList = this.getGdnDtlList(gdnHeader, input);
        //1.2 获取sn信息
        List<String> snList = input.getSnItems().stream().filter(SubmitOtherOutItemRequest::isScan).map(SubmitOtherOutItemRequest::getSn).distinct().collect(Collectors.toList());
        List<Goods> goodsList = snList.size() > 0 ? this.goodsService.selectListByListSn(snList) : new ArrayList<>(0);
        if (snList.size() > 0) {
            if (goodsList.size() != snList.size()) {
                List<String> existSn = goodsList.stream().map(Goods::getGoodsSn).distinct().collect(Collectors.toList());
                List<String> notExistSn = snList.stream().filter(t -> !existSn.contains(t)).collect(Collectors.toList());
                Shift.fatal("以下sn没有找不到信息:[" + String.join(",", notExistSn) + "]");
            }
            List<String> whsGoodsList = goodsList.stream()
                    .filter(t -> !t.getWhsId().equals(input.getWhsId())).map(Goods::getGoodsSn)
                    .collect(Collectors.toList());
            if (whsGoodsList.size() > 0) {
                Shift.fatal("以下sn不在所选仓库中:[" + String.join(",", whsGoodsList) + "]");
            }
        }
        //1.3 生成出库凭证
        TrsnHeader trsnHeader = this.setTrsnHeader(gdnHeader, gdnDtlList.get(0));
        //2. 数据处理
        List<Long> gdnIdList = input.getSnItems().stream().map(SubmitOtherOutItemRequest::getGdnDtlId).distinct().collect(Collectors.toList());
        List<GdnDtlGoods> gdnDtlGoodsList = new ArrayList<>();
        List<TrsnDtl> trsnDtlList = new ArrayList<>();

        List<TrsnDtlGoods> trsnDtlGoodsList = new ArrayList<>();

        List<BigDecimal> notSnQtyList = new ArrayList<>();
        List<BigDecimal> qtyList = new ArrayList<>();
        gdnIdList.forEach(t -> {
            notSnQtyList.add(BigDecimal.ZERO);
        });
        for (int i = 0; i < gdnIdList.size(); i++) {
            Long gdnDtlId = gdnIdList.get(i);
            GdnDtl gdnDtl = gdnDtlList.stream().filter(t -> t.getGdnDtlId().equals(gdnDtlId)).findFirst().get();
            BigDecimal qty = BigDecimal.ZERO;
            TrsnDtl trsnDtl = this.setTrsnDtl(gdnHeader, gdnDtl);
            trsnDtl.setNoteNo(trsnHeader.getNoteNo());
            trsnDtl.setNoteLineNo(String.valueOf(i + 1));
            trsnDtlList.add(trsnDtl);
            List<SubmitOtherOutItemRequest> collect = input.getSnItems().stream()
                    .filter(t -> t.getGdnDtlId().equals(gdnDtlId))
                    .collect(Collectors.toList());
            for (SubmitOtherOutItemRequest item : collect) {
                qty = qty.add(item.getQty());
                if (item.isScan()) {
                    Goods firstGoods = goodsList.stream().filter(t -> t.getGoodsSn().equals(item.getSn())).findFirst().get();
                    GdnDtlGoods gdnDtlGoods = this.setGdnDtlGoods(gdnDtl, firstGoods);
                    gdnDtlGoodsList.add(gdnDtlGoods);
                    TrsnDtlGoods trsnDtlGoods = this.setTrsnDtlGoods(trsnDtl, gdnDtlGoods);
                    trsnDtlGoods.setTrsnDtlId(i);
                    trsnDtlGoodsList.add(trsnDtlGoods);
                } else {
                    notSnQtyList.set(i, notSnQtyList.get(i).add(item.getQty()));
                }
            }
            qtyList.add(qty);
            //数量赋值
            gdnDtl.setShipQty(gdnDtl.getShipQty().add(qty));
            if (gdnDtl.getShipQty().compareTo(gdnDtl.getOrderQty()) > 0) {
            	Shift.fatal("不允许超发!");
            }
            trsnDtl.setQty(qty);
        }
        for (int i = 0; i < trsnDtlList.size(); i++) {
            BigDecimal qty = notSnQtyList.get(i);
            if (qty.compareTo(BigDecimal.ZERO) <= 0)
                continue;
            TrsnDtl trsnDtl = trsnDtlList.get(i);
            GdnDtl gdnDtl = gdnDtlList.stream().filter(t -> trsnDtl.getFmDocLine().equals(t.getGdnLineNo().toString())).findFirst().get();

            InventoryConditionDto conditionDto = new InventoryConditionDto();
            conditionDto.setWhsId(gdnDtl.getWhsId());
            conditionDto.setFactory(gdnHeader.getFactory());
            conditionDto.setInvAttr(gdnDtl.getInvAttr());
            conditionDto.setOwner(gdnHeader.getOwner());
            conditionDto.setLotNo(gdnDtl.getLotno());
            conditionDto.setSkuId(gdnDtl.getSkuId());
            conditionDto.setVendorId(gdnDtl.getVendor());
            conditionDto.setQaStatus(gdnDtl.getQaStatus());
            this.inventoryService.otherStockOutValidation(conditionDto, qty);
        }
        //4. 保存数据
        //4.1 保存 发货表头
        gdnHeader.setModifyUser(input.getUserName());
        gdnHeader.setModifyTime(new Date());
        if (gdnDtlList.stream().filter(t -> t.getOrderQty().compareTo(t.getShipQty()) == 0).count() == gdnDtlList.size()) {
            gdnHeader.setStatus(GdnHeaderStatusEnum.CLOSE.getCode());
        } else {
            gdnHeader.setStatus(GdnHeaderStatusEnum.DOING.getCode());
        }
        this.gdnManagerService.getGdnHeaderMapper().updateById(gdnHeader);
        //4.2 保存 发货明细
        if (gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CLOSE.getCode())) {
            gdnDtlList.forEach(gdnDtl -> {
                gdnDtl.setStatus(GdnDtlStatusEnum.CLOSE.getCode());
                gdnDtl.setModifyUser(input.getUserName());
                gdnDtl.setModifyTime(new Date());
                this.gdnManagerService.getGdnDtlMapper().updateById(gdnDtl);
            });
        } else {
            gdnDtlList.stream().filter(t -> gdnIdList.contains(t.getGdnDtlId())).forEach(gdnDtl -> {
                if (gdnDtl.getOrderQty().compareTo(gdnDtl.getShipQty()) == 0) {
                    gdnDtl.setStatus(GdnDtlStatusEnum.SHIPMENTED.getCode());
                } else {
                    gdnDtl.setStatus(GdnDtlStatusEnum.SHIPMENTING.getCode());
                }
                gdnDtl.setModifyUser(input.getUserName());
                gdnDtl.setModifyTime(new Date());
                this.gdnManagerService.getGdnDtlMapper().updateById(gdnDtl);
            });
        }
        //4.3 保存 发货明细->goods
        gdnDtlGoodsList.forEach(gdnDtlGoods -> {
            gdnDtlGoods.setCreateUser(input.getUserName());
            gdnDtlGoods.setModifyUser(input.getUserName());
            gdnDtlGoods.setCreateTime(new Date());
            gdnDtlGoods.setModifyTime(new Date());
            this.gdnManagerService.getGdnDtlGoodsMapper().insert(gdnDtlGoods);
        });
        //4.4 出库凭证 /4.5 出库凭证明细
        trsnDtlList.forEach(trsnDtl -> {
            trsnDtl.setCreateUser(input.getUserName());
            trsnDtl.setModifyUser(input.getUserName());
            trsnDtl.setCreateTime(new Date());
            trsnDtl.setModifyTime(new Date());
        });
        this.trsnManagerService.insertTrsnInfo(trsnHeader, trsnDtlList);
        //4.6 出库凭证货品清单
        trsnDtlGoodsList.forEach(trsnDtlGoods -> {
            //获取对应的 凭证行
            TrsnDtl trsnDtl = trsnDtlList.get(trsnDtlGoods.getTrsnDtlId());
            trsnDtlGoods.setTrsnDtlId(trsnDtl.getTrsnDtlId());
            trsnDtlGoods.setCreateUser(input.getUserName());
            trsnDtlGoods.setModifyUser(input.getUserName());
            trsnDtlGoods.setCreateTime(new Date());
            trsnDtlGoods.setModifyTime(new Date());
            this.trsnManagerService.getTrsnDtlGoodsMapper().insert(trsnDtlGoods);
        });
        //4.7 sn 挪到库存日志中
        goodsList.forEach(goods -> {
            GoodsHis goodsHis = new GoodsHis();
            //转移数据
            BeanUtil.copy(goods, goodsHis);
            goodsHis.setLogCreateTime(new Date());
            goodsHis.setLogCreateUser(input.getUserName());
            this.goodsService.getGoodsHisMapper().insert(goodsHis);
            this.goodsService.getGoodsMapper().deleteById(goods.getGoodsId());

            GoodsLog goodsLog = setGoodsLog(gdnHeader, goods);
            this.goodsService.insertGoodsLog(goodsLog);
        });
        //4.8 单据操作日志
        trsnDtlList.forEach(trsnDtl -> {
            //1. 获取出库单明细
            GdnDtl gdnDtl = gdnDtlList.stream().filter(t -> trsnDtl.getFmDocLine().equals(t.getGdnLineNo().toString())).findFirst().get();
            DocLog docLog = this.setDocLog(gdnHeader, gdnDtl, trsnDtl);
            this.docLogService.addNewOne(docLog);
        });
        //4.9 库存变更
        this.handleInventory(gdnHeader, gdnDtlList, trsnHeader, trsnDtlList, trsnDtlGoodsList, notSnQtyList, goodsList);

        //Shift.fatal("错误,人为操作 error!");
        //返包出库  每次都回传sap
        if (gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CLOSE.getCode())) {
            isClose = true;
        }
        if (gdnHeader.getGdnType().equals(GdnTypeEnum.D03.getCode())) {
            this.callbackService.Callback(gdnHeader, trsnHeader);
        }
        if (isClose && !gdnHeader.getGdnType().equals(GdnTypeEnum.D03.getCode())) {
            this.callbackService.Callback(gdnHeader);
        }
        return isClose;
    }

    private void handleInventory(GdnHeader gdnHeader, List<GdnDtl> gdnDtlList,
                                 TrsnHeader trsnHeader, List<TrsnDtl> trsnDtlList, List<TrsnDtlGoods> trsnDtlGoodsList,
                                 List<BigDecimal> notSnQtyList, List<Goods> goodsList) throws Exception {
        //sn库存扣减
        if (goodsList.size() > 0) {
            this.handleInventory_SN(gdnHeader, trsnHeader, trsnDtlList, trsnDtlGoodsList, goodsList);
        }
        //无sn库存扣减
        if (notSnQtyList.size() > 0) {
            this.handleInventory_Not_SN(gdnHeader, gdnDtlList, trsnHeader, trsnDtlList, notSnQtyList);
        }
    }

    private void handleInventory_Not_SN(GdnHeader gdnHeader, List<GdnDtl> gdnDtlList, TrsnHeader trsnHeader,
                                        List<TrsnDtl> trsnDtlList, List<BigDecimal> notSnQtyList) throws Exception {
        for (int i = 0; i < notSnQtyList.size(); i++) {
            if (notSnQtyList.get(i).compareTo(BigDecimal.ZERO) <= 0)
                continue;
            TrsnDtl trsnDtl = trsnDtlList.get(i);
            GdnDtl gdnDtl = gdnDtlList.stream().filter(t -> trsnDtl.getFmDocLine().equals(t.getGdnLineNo().toString())).findFirst().get();

            UpdateInventoryInputDto fmInput = new UpdateInventoryInputDto();
            fmInput.setWhsId(trsnHeader.getWhsId());
            fmInput.setOwner(trsnHeader.getOwner());
            fmInput.setSkuId(trsnDtl.getSkuId());
            fmInput.setFactory(trsnHeader.getFactory());
            fmInput.setVendorId(trsnHeader.getVendorId());
            fmInput.setLotNo(gdnDtl.getLotno());
            fmInput.setInvAttr(gdnDtl.getInvAttr());
            fmInput.setQaStatus(gdnDtl.getQaStatus());
            fmInput.setQty(notSnQtyList.get(i).negate());
            fmInput.setUnit(gdnDtl.getUnit());
            fmInput.setOperator(gdnHeader.getModifyUser());

            InvTrans log = new InvTrans();
            log.setOpCode(OpCodeEnum.SHIPPED.getCode());
            log.setDocType(gdnHeader.getGdnType());
            log.setDocNo(gdnHeader.getGdnNo());
            log.setDocLineNo(trsnDtl.getFmDocLine());
            log.setTrsnNo(trsnHeader.getNoteNo());
            log.setTrsnLineNo(trsnDtl.getNoteLineNo());
            log.setStatus(InvTransStatusEnum.FINISHED.getCode());
            log.setTransTime(new Date());
            log.setOperator(trsnDtl.getModifyUser());
            log.setTransQty(notSnQtyList.get(i));
            log.setCreateTime(new Date());
            log.setModifyTime(new Date());
            log.setCreateUser(trsnDtl.getModifyUser());
            log.setModifyUser(trsnDtl.getModifyUser());
            this.inventoryService.inventoryOut(fmInput, log);

        }
    }

    private void handleInventory_SN(GdnHeader gdnHeader, TrsnHeader trsnHeader,
                                    List<TrsnDtl> trsnDtlList, List<TrsnDtlGoods> trsnDtlGoodsList,
                                    List<Goods> goodsList) throws Exception {
        for (TrsnDtl trsnDtl : trsnDtlList) {
            List<String> snList = trsnDtlGoodsList.stream().filter(t -> t.getTrsnDtlId().equals(trsnDtl.getTrsnDtlId())).map(TrsnDtlGoods::getGoodsSn).collect(Collectors.toList());
            Map<String, List<Goods>> collect = goodsList.stream().filter(t -> snList.contains(t.getGoodsSn())).collect(
                    Collectors.groupingBy(t ->
                                    t.getWhsId() + "#" +
                                            t.getLocId() + "#" +
                                            t.getOwner() + "#" +
                                            t.getSkuId() + "#" +
                                            t.getFactory() + "#" +
                                            t.getVendorId() + "#" +
                                            t.getLotno() + "#" +
                                            t.getInvAttr() + "#" +
                                            t.getQaStatus() + "#",
                            Collectors.toList()));

            for (String key : collect.keySet()) {
                Goods firstGoods = collect.get(key).get(0);
                BigDecimal qty = collect.get(key).stream().map(Goods::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);

                UpdateInventoryInputDto fmInput = new UpdateInventoryInputDto();
                fmInput.setWhsId(firstGoods.getWhsId());
                fmInput.setLocId(firstGoods.getLocId());
                fmInput.setOwner(firstGoods.getOwner());
                fmInput.setSkuId(firstGoods.getSkuId());
                fmInput.setFactory(firstGoods.getFactory());
                fmInput.setVendorId(firstGoods.getVendorId());
                fmInput.setLotNo(firstGoods.getLotno());
                fmInput.setInvAttr(firstGoods.getInvAttr());
                fmInput.setQaStatus(firstGoods.getQaStatus());
                fmInput.setQty(qty.negate());
                fmInput.setUnit(firstGoods.getUnit());
                fmInput.setOperator(gdnHeader.getModifyUser());

                InvTrans log = new InvTrans();
                log.setOpCode(OpCodeEnum.SHIPPED.getCode());
                log.setDocType(gdnHeader.getGdnType());
                log.setDocNo(gdnHeader.getGdnNo());
                log.setDocLineNo(trsnDtl.getFmDocLine());
                log.setTrsnNo(trsnHeader.getNoteNo());
                log.setTrsnLineNo(trsnDtl.getNoteLineNo());
                log.setStatus(InvTransStatusEnum.FINISHED.getCode());
                log.setTransTime(new Date());
                log.setOperator(trsnDtl.getModifyUser());
                log.setTransQty(qty);
                log.setCreateTime(new Date());
                log.setModifyTime(new Date());
                log.setCreateUser(trsnDtl.getModifyUser());
                log.setModifyUser(trsnDtl.getModifyUser());
                this.inventoryService.inventoryOut(fmInput, log);
            }

        }
    }

    private DocLog setDocLog(GdnHeader gdnHeader, GdnDtl gdnDtl, TrsnDtl trsnDtl) {
        DocLog docLog = new DocLog();
        docLog.setDocType(gdnHeader.getGdnType());
        docLog.setDocNo(gdnHeader.getGdnNo());
        docLog.setDocLineNo(String.valueOf(gdnDtl.getGdnLineNo()));
        docLog.setOpCode("Shipped");
        docLog.setOperator(trsnDtl.getModifyUser());
        docLog.setOpTime(new Date());
        docLog.setContent("出库数量增加:" + trsnDtl.getQty());
        docLog.setUdf1("/");
        docLog.setUdf2("/");
        docLog.setUdf3("/");
        docLog.setUdf4("/");
        docLog.setUdf5("/");
        docLog.setComments(trsnDtl.getComments());
        docLog.setCreateTime(new Date());
        docLog.setCreateUser(trsnDtl.getModifyUser());
        docLog.setModifyTime(new Date());
        docLog.setModifyUser(trsnDtl.getModifyUser());
        return docLog;
    }

    private List<GdnDtl> getGdnDtlList(GdnHeader gdnHeader, SubmitOtherOutInputRequest input) {
        List<GdnDtl> gdnDtlList = this.gdnManagerService.selectDetailsByHeaderId(gdnHeader.getGdnHeaderId());
        for (GdnDtl gdnDtl : gdnDtlList) {
            if (input.getSnItems().stream().filter(t -> t.getGdnDtlId().equals(gdnDtl.getGdnDtlId())).count() == 0)
                continue;
            if (!("".equals(gdnDtl.getLotAttr1()) || gdnDtl.getLotAttr1() == null))
                continue;
            if (gdnDtl.getShipQty() == null) gdnDtl.setShipQty(BigDecimal.ZERO);
            Lotno lotno;
            if (gdnDtl.getLotno() == null)
                lotno = lotnoService.getLotnoByGdnDtlId(gdnDtl.getGdnDtlId());
            else
                lotno = lotnoService.selectByLotno(gdnDtl.getLotno());
            gdnDtl.setLotno(lotno.getLotno());
            gdnDtl.setLotAttr1(lotno.getLotAttr1());
            gdnDtl.setLotAttr2(lotno.getLotAttr2());
            gdnDtl.setLotAttr3(lotno.getLotAttr3());
            gdnDtl.setLotAttr4(lotno.getLotAttr4());
            gdnDtl.setLotAttr5(lotno.getLotAttr5());
            gdnDtl.setLotAttr6(lotno.getLotAttr6());
            gdnDtl.setLotAttr7(lotno.getLotAttr7());
            gdnDtl.setLotAttr8(lotno.getLotAttr8());
            gdnDtl.setLotAttr9(lotno.getLotAttr9());
            gdnDtl.setLotAttr10(lotno.getLotAttr10());
            gdnDtl.setLotAttr11(lotno.getLotAttr11());
            gdnDtl.setLotAttr12(lotno.getLotAttr12());
            gdnDtl.setModifyUser(input.getUserName());
        }
        return gdnDtlList;
    }

    private GdnDtlGoods setGdnDtlGoods(GdnDtl firstGdnDtl, Goods firstGoods) {
        GdnDtlGoods item = new GdnDtlGoods();
        item.setGdnDtlId(firstGdnDtl.getGdnDtlId());
        item.setGoodsId(firstGoods.getGoodsId());
        item.setGoodsSn(firstGoods.getGoodsSn());
        item.setQaStatus(firstGoods.getQaStatus());
        item.setQty(firstGoods.getQty());
        item.setComments("");
        item.setStatus(firstGoods.getStatus());
        item.setCreateUser(firstGdnDtl.getModifyUser());
        item.setModifyUser(firstGdnDtl.getModifyUser());
        item.setCreateTime(new Date());
        item.setModifyTime(new Date());
        return item;
    }

    /**
     * 强制关单
     *
     * @param billNo
     * @param userName
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void close(String billNo, String userName) {
        //1. 获取单据
        GdnHeader gdnHeader = this.gdnManagerService.selectByNo(billNo);
        if (gdnHeader == null) {
            Shift.fatal("没有找到单据号!");
        }
        if (gdnHeader.getStatus().equals(GdnHeaderStatusEnum.FINISH.getCode()) ||
                gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CLOSE.getCode()) ||
                gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CANCEL.getCode())) {
            Shift.fatal("单据已经完成/关闭,无需再次关单!");
        }
        gdnHeader.setStatus(GdnHeaderStatusEnum.CLOSE.getCode());
        gdnHeader.setModifyTime(new Date());
        gdnHeader.setModifyUser(userName);
        this.gdnManagerService.getGdnHeaderMapper().updateById(gdnHeader);
        //1.1 获取单据明细
        List<GdnDtl> gdnDtlList = this.gdnManagerService.selectDetailsByHeaderId(gdnHeader.getGdnHeaderId());
        gdnDtlList.forEach(item -> {
            if (!item.getStatus().equals(GdnDtlStatusEnum.CLOSE.getCode())) {
                item.setStatus(GdnDtlStatusEnum.CLOSE.getCode());
                item.setModifyTime(new Date());
                item.setModifyUser(userName);
                this.gdnManagerService.getGdnDtlMapper().updateById(item);
            }
        });
        this.callbackService.Callback(gdnHeader);
    }

    private GoodsLog setGoodsLog(GdnHeader gdnHeader, Goods item) {
        GoodsLog goodsLog = new GoodsLog();
        goodsLog.setCreateTime(new Date());
        goodsLog.setCreateUser(gdnHeader.getCreateUser());
        goodsLog.setGoodsSn(item.getGoodsSn());
        goodsLog.setOpCode("Shipped");
        goodsLog.setOperator(gdnHeader.getCreateUser());
        goodsLog.setReason("货品出库");
        goodsLog.setUpdateContent("未更新的:在sn货品清单中,更新后的:删除sn货品清单,数据挪到货品清单历史中");
        return goodsLog;
    }

    private TrsnDtlGoods setTrsnDtlGoods(TrsnDtl trsnDtl, GdnDtlGoods gdnDtlGoods) {
        TrsnDtlGoods item = new TrsnDtlGoods();
        item.setTrsnDtlId(trsnDtl.getTrsnDtlId());
        item.setGoodsId(gdnDtlGoods.getGoodsId());
        item.setGoodsSn(gdnDtlGoods.getGoodsSn());
        item.setQaStatus(gdnDtlGoods.getQaStatus());
        item.setQty(gdnDtlGoods.getQty());
        item.setComments("");
        item.setStatus(TrsnDtlGoodsStatusEnum.RECEIVED.getCode());
        item.setQaStatus(gdnDtlGoods.getQaStatus());
        item.setCreateUser(trsnDtl.getCreateUser());
        item.setModifyUser(trsnDtl.getCreateUser());
        item.setCreateTime(new Date());
        item.setModifyTime(new Date());
        return item;
    }

    private TrsnDtl setTrsnDtl(GdnHeader gdnHeader, GdnDtl gdnDtl) {
        TrsnDtl item = new TrsnDtl();

        item.setNoteLineNo(String.valueOf(gdnDtl.getGdnLineNo()));
        item.setInvUpdateFlag(TrsnDtlInvUpdateFlagEnum.OUT.getCode());
        item.setFmDoc(gdnHeader.getGdnNo());
        item.setFmDocLine(String.valueOf(gdnDtl.getGdnLineNo()));
        item.setSkuId(gdnDtl.getSkuId());
        item.setQty(BigDecimal.ZERO);
        item.setUnit(gdnDtl.getUnit());
        item.setQaStatus(gdnDtl.getQaStatus());
        item.setInvAttr(gdnDtl.getInvAttr());
        item.setLotAttr1(gdnDtl.getLotAttr1());
        item.setLotAttr2(gdnDtl.getLotAttr2());
        item.setLotAttr3(gdnDtl.getLotAttr3());
        item.setLotAttr4(gdnDtl.getLotAttr4());
        item.setLotAttr5(gdnDtl.getLotAttr5());
        item.setLotAttr6(gdnDtl.getLotAttr6());
        item.setLotAttr7(gdnDtl.getLotAttr7());
        item.setLotAttr8(gdnDtl.getLotAttr8());
        item.setLotAttr9(gdnDtl.getLotAttr9());
        item.setLotAttr10(gdnDtl.getLotAttr10());
        item.setLotAttr11(gdnDtl.getLotAttr11());
        item.setLotAttr12(gdnDtl.getLotAttr12());
        item.setStatus(TrsnDtlStatusEnum.CMP.getCode());
        item.setCreateTime(new Date());
        item.setModifyTime(new Date());
        item.setModifyUser(gdnHeader.getModifyUser());
        item.setCreateUser(gdnHeader.getModifyUser());
        return item;
    }

    private TrsnHeader setTrsnHeader(GdnHeader gdnHeader, GdnDtl firstDetail) {
        TrsnHeader item = new TrsnHeader();
        item.setNoteNo(basicSimpleCodeRuleApi.GenerateCode(trsnRuleCode));
        item.setNoteType(TrsnNoteTypeEnum.OUT_STORAGE.getCode()); //凭证类型
        item.setOwner(gdnHeader.getOwner());
        item.setFactory(gdnHeader.getFactory());
        item.setWhsId(firstDetail.getWhsId());
        item.setVendorId(firstDetail.getVendor());
        //来源单据
        item.setFmDoc(gdnHeader.getGdnNo());
        item.setStatus(TrsnHeaderStatusEnum.CRT.getCode());
        item.setCreateTime(new Date());
        item.setModifyTime(new Date());
        item.setModifyUser(gdnHeader.getModifyUser());
        item.setCreateUser(gdnHeader.getModifyUser());
        return item;
    }
}
